#region Using directives

using System;
using System.Collections;
//using System.Threading;
using System.Windows.Forms;
using System.Text;
using System.IO;
using System.Reflection;

using FighterChessEngine.Event;
using FighterChessEngine.Enum;
using FighterChessEngine.FC_Fighter;
using FighterChessEngine.FC_Fighter.Fighters;
using FighterChessEngine.FC_Player;

#endregion

namespace FighterChessEngine.FC_Board
{
    /// <summary>
    /// Summary description for Board.
    /// </summary>
    public class Board
    {
        #region Atributes
        private Fighter[,] fighters;
        private Square[,] squares;

        // use for play game
        //private Square activeSquare;
        private Fighter activeFighter;
        private Owner activePlayer;
        private Timer timer;
        private Player player1;
        private Player player2;
        private Player winner;
        private Player loser;
        #endregion

        #region Properties
        public Fighter[,] Fighters
        {
            get { return fighters; }
            set { fighters = value; }
        }
        public Square[,] Squares
        {
            get { return squares; }
            set { squares = value; }
        }
        public Fighter ActiveFighter
        {
            get { return activeFighter; }
        }
        public Owner ActivePlayer
        {
            get { return activePlayer; }
            set { activePlayer = value; }
        }
        public Player Player1
        {
            get { return player1; }
            set { player1 = value; }
        }
        public Player Player2
        {
            get { return player2; }
            set { player2 = value; }
        }
        public bool GameIsEnd
        {
            get
            {
                return winner == null ? false : true;
            }
        }
        #endregion

        #region Methods
        public Board()
        {
            activePlayer = Owner.White; // white go first
            timer = new Timer();
            timer.Interval = 125;

            InitSquares();
            AddFighters();
            InitEachFighter();
        }

        public Board(Player p1, Player p2)
            : this()
        {
            winner = null;
            loser = null;
            player1 = new Player(p1.Name, p1.Owner, p1.Active);
            player2 = new Player(p2.Name, p2.Owner, p2.Active);
            if (player1.Active)
                activePlayer = player1.Owner;
            else if (player2.Active)
                activePlayer = player2.Owner;
        }

        public Board(string filename)
        {
            timer = new Timer();
            timer.Interval = 125;
            winner = null;
            loser = null;

            FileStream fs = new FileStream(filename, FileMode.Open);
            StreamReader sr = new StreamReader(fs);

            // load players
            player1 = new Player(sr.ReadLine());
            player2 = new Player(sr.ReadLine());

            if (player1.Active)
                activePlayer = player1.Owner;
            else if (player2.Active)
                activePlayer = player2.Owner;

            // load squares
            char[] sepCol = { '\t' };
            string[,] strSquares = new string[8, 8];
            for (int row = 0; row < 8; row++)
            {
                string line = sr.ReadLine();
                string[] temp = line.Split(sepCol);
                for (int col = 0; col < 8; col++)
                    strSquares[row, col] = temp[col];
            }
            InitSquares(strSquares);

            // load fighters
            string[,] strFighters = new string[8, 8];
            for (int row = 0; row < 8; row++)
            {
                string line = sr.ReadLine();
                string[] temp = line.Split(sepCol);
                for (int col = 0; col < 8; col++)
                    strFighters[row, col] = temp[col];
            }
            AddFighters(strFighters);
            InitEachFighter();

            sr.Close();
            fs.Close();
        }

        public void Save(string filename)
        {
            string sepRow = "\r\n";
            string sepCol = "\t";
            StringBuilder sb = new StringBuilder();
            // save players
            sb.Append(player1.ToSaveString());
            sb.Append(sepRow);
            sb.Append(player2.ToSaveString());
            sb.Append(sepRow);

            // save squares
            for (int row = 0; row < 8; row++)
            {
                for (int col = 0; col < 8; col++)
                {
                    sb.Append(squares[row, col].ToSaveString());
                    sb.Append(sepCol);
                }
                sb.Append(sepRow);
            }

            // save fighters
            for (int row = 0; row < 8; row++)
            {
                for (int col = 0; col < 8; col++)
                {
                    if (fighters[row, col] == null)
                        sb.Append("NULL");
                    else
                        sb.Append(fighters[row, col].ToSaveString());
                    sb.Append(sepCol);
                }
                sb.Append(sepRow);
            }

            // write to file
            FileStream fs = new FileStream(filename, FileMode.Create);
            StreamWriter sw = new StreamWriter(fs);
            sw.Write(sb.ToString());
            sw.Flush();
            sw.Close();
            fs.Close();
        }

        public void UpdateNextPosAllFighters()
        {
            for (int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                    if (fighters[row, col] != null)
                        fighters[row, col].UpdateNextPositions(fighters);
        }

        public Fighter Click(int row, int col)
        {
            if (winner != null) //  game over already
                return null;

            if (activeFighter == null) // don't have active fighter selected
            {
                if (fighters[row, col] != null)
                {
                    if (fighters[row, col].Owner == activePlayer)
                    {
                        SelectFighter(row, col);
                        return activeFighter;
                    }
                }
            }
            else if (activeFighter != null) // have active fighter selected
            {
                if (activeFighter.Stance == Stance.Move) // is moving
                    return activeFighter;

                if (fighters[row, col] != null && fighters[row, col].Owner == activeFighter.Owner)// select another fighter
                {
                    SelectFighter(row, col);
                    return activeFighter;
                }

                if (activeFighter.CanMoveTo(row, col)) // move
                {
                    if (fighters[row, col] == null) // move
                    {
                        activeFighter.MoveTo(row, col);
                        
                        timer.Tick += new EventHandler(timer_Tick_WaitForMove);
                        timer.Enabled = true;
                    }
                    else // fight
                    {
                        // fight ...
                        int fightResult = activeFighter.Fight(fighters[row, col]);
                        if (fightResult == 0) // both die
                            activeFighter = null;
                        else if (fightResult == 1)
                        {
                            // ... then move
                            activeFighter.MoveTo(row, col);
                            
                            timer.Tick += new EventHandler(timer_Tick_WaitForMove);
                            timer.Enabled = true;
                        }
                    }
                    //UpdateNextPosAllFighters();
                    ChangeTurn();
                }
                else // can't move => deselect
                {
                    if (row == activeFighter.CurrentPos.Row && col == activeFighter.CurrentPos.Col)
                        return activeFighter;

                    DeselectActiveFighter();
                }
            }

            return null;
        }

        public Fighter GetFighterAt(int row, int col)
        {
            return fighters[row, col] != null ? fighters[row, col] : null;
        }
        #endregion

        #region Event
        public delegate void EventFighterMoveHandler(Fighter fighter, EventMoveArgs e);
        public event EventFighterMoveHandler FighterMove;
        public delegate void EventFighterSitDownHandler(Fighter fighter);
        public event EventFighterSitDownHandler FighterSitDown;
        public delegate void EventFighterStandUpHandler(Fighter fighter);
        public event EventFighterStandUpHandler FighterStandUp;
        public delegate void EventFighterRestoreHPHandler(Fighter fighter);
        public event EventFighterRestoreHPHandler FighterRestoreHP;
        public delegate void EventFighterLevelUpHandler(Fighter fighter, int increment);
        public event EventFighterLevelUpHandler FighterLevelUp;
        public delegate void EventFighterClickedHandler(Fighter fighter);
        public event EventFighterClickedHandler FighterClicked;
        public delegate void EventFighterKilledHandler(Fighter victim, Fighter killer);
        public event EventFighterKilledHandler FighterKilled;
        public delegate void EventFightersFightHandler(Fighter attacker, Fighter defender);
        public event EventFightersFightHandler FightersFight;
        public delegate void EventEndGameHandler(Player winner, Player loser);
        public event EventEndGameHandler GameOver;

        public delegate void EventFighterMoveToRandomSquareHandler(Fighter fighter, SquareType fate);
        public event EventFighterMoveToRandomSquareHandler FighterMoveToRandomSquare;

        void Board_ActionMove(Fighter fighter, FighterChessEngine.Event.EventMoveArgs e)
        {
            MoveFighter(fighter, e.Move);
            FighterMove(fighter, e);
        }

        void Board_ActionStandUp(Fighter fighter)
        {
            FighterStandUp(fighter);
        }

        void Board_ActionSitDown(Fighter fighter)
        {
            FighterSitDown(fighter);
        }

        void Board_ActionClick(Fighter fighter)
        {
            for (int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                    if (fighters[row, col] != null
                        && (row != fighter.CurrentPos.Row || col != fighter.CurrentPos.Col)
                        && fighters[row, col].Stance == Stance.Stand)
                        fighters[row, col].SitDown();
            //fighters[row, col].Stance = Stance.Sit;
            FighterClicked(fighter);
        }

        void Board_ActionFight(Fighter attacker, Fighter defender)
        {
            FightersFight(attacker, defender);
        }

        void Board_ActionDie(Fighter victim, Fighter killer)
        {
            fighters[victim.CurrentPos.Row, victim.CurrentPos.Col] = null;
            UpdateNextPosAllFighters();

            FighterKilled(victim, killer);

            if (victim.Type == FighterType.King)
                EndGame(victim.Owner);
        }

        void Board_ActionLevelUp(Fighter fighter, int increment)
        {
            FighterLevelUp(fighter, increment);
        }

        void Board_ActionRestoreHP(Fighter fighter)
        {
            FighterRestoreHP(fighter);
        }

        // wait timer
        void timer_Tick_WaitForMove(object sender, EventArgs e)
        {
            if (activeFighter.Stance != Stance.Move)
            {
                timer.Enabled = false;
                timer.Tick -= new EventHandler(timer_Tick_WaitForMove);

                int row = activeFighter.CurrentPos.Row;
                int col = activeFighter.CurrentPos.Col;
                FighterArriveAt(row, col);
                if (activeFighter != null)
                    DeselectActiveFighter();
            }
        }

        void timer_Tick_WaitToFight(object sender, EventArgs e)
        {
            if (activeFighter.Stance != Stance.Move)
            {
                timer.Enabled = false;
                timer.Tick -= new EventHandler(timer_Tick_WaitToFight);

                // ... then fight
                int row = activeFighter.CurrentPos.Row;
                int col = activeFighter.CurrentPos.Col;
                int fightResult = activeFighter.Fight(fighters[row, col]);

                // move on real board
                

                FighterArriveAt(row, col);

                if (fightResult == 0) // both die
                    activeFighter = null;

                if (activeFighter != null)
                    DeselectActiveFighter();
            }
        }
        #endregion

        #region Support Funcs
        /*private void Init()
        {
            InitSquares();
            AddFighters();
            InitEachFighter();
        }*/

        private void InitSquares()
        {
            squares = new Square[8, 8];
            for (int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                    squares[row, col] = new Square(row, col, SquareType.Normal);

            squares[0, 0].Type = SquareType.HpRestoreWhite;
            squares[7, 0].Type = SquareType.HpRestoreWhite;
            squares[0, 7].Type = SquareType.HpRestoreBlack;
            squares[7, 7].Type = SquareType.HpRestoreBlack;

            squares[4, 0].Type = SquareType.LevelUpBlack;
            squares[4, 7].Type = SquareType.LevelUpWhite;

            Random rand = new Random();
            squares[rand.Next(0, 7), rand.Next(2, 5)].Type = SquareType.Random;
        }

        private void InitSquares(string[,] strSquares)
        {
            squares = new Square[8, 8];
            for (int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                    squares[row, col] = new Square(strSquares[row,col]);
        }

        private void AddFighters()
        {
            fighters = new Fighter[8, 8];
            for (int row = 0; row < 8; row++)
                switch (row)
                {
                    case 0:
                        AddPawns(row);
                        AddRooks(row);
                        break;
                    case 1:
                        AddPawns(row);
                        AddKnights(row);
                        break;
                    case 2:
                        AddPawns(row);
                        AddBishops(row);
                        break;
                    case 3:
                        AddPawns(row);
                        AddQueens(row);
                        break;
                    case 4:
                        AddPawns(row);
                        AddKings(row);
                        break;
                    case 5:
                        AddPawns(row);
                        AddBishops(row);
                        break;
                    case 6:
                        AddPawns(row);
                        AddKnights(row);
                        break;
                    case 7:
                        AddPawns(row);
                        AddRooks(row);
                        break;
                    default: break;
                }
        }

        private void AddFighters(string[,] strFighters)
        {
            fighters = new Fighter[8, 8];
            for (int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                {
                    if (strFighters[row, col] == "NULL")
                        continue;

                    // strFighters[row, col] : Symbol,Owner,Lvl,HP,Facing,Row,Col
                    // ex: KI,1,10,90,1,4,1
                    char[] sep = { ',' };
                    string[] arrInfos = strFighters[row, col].Split(sep);
                    switch (arrInfos[0])
                    {
                        case "KI":
                            fighters[row, col] = new King(strFighters[row, col]);
                            break;
                        case "R":
                            fighters[row, col] = new Rook(strFighters[row, col]);
                            break;
                        case "B":
                            fighters[row, col] = new Bishop(strFighters[row, col]);
                            break;
                        case "K":
                            fighters[row, col] = new Knight(strFighters[row, col]);
                            break;
                        case "Q":
                            fighters[row, col] = new Queen(strFighters[row, col]);
                            break;
                        case "P":
                            fighters[row, col] = new Pawn(strFighters[row, col]);
                            break;
                    }
                }
        }

        private void InitEachFighter()
        {
            for (int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                    if (fighters[row, col] != null)
                    {
                        // init next positions
                        fighters[row, col].UpdateNextPositions(fighters);

                        // init events
                        fighters[row, col].ActionMove += new Fighter.EventMoveHandler(Board_ActionMove);
                        fighters[row, col].ActionSitDown += new Fighter.EventSitDownHandler(Board_ActionSitDown);
                        fighters[row, col].ActionStandUp += new Fighter.EventStandUpHandler(Board_ActionStandUp);
                        fighters[row, col].ActionClick += new Fighter.EventClickHandler(Board_ActionClick);
                        fighters[row, col].ActionFight += new Fighter.EventFightHandler(Board_ActionFight);
                        fighters[row, col].ActionDie += new Fighter.EventDieHandler(Board_ActionDie);
                        fighters[row, col].ActionRestoreHP += new Fighter.EventHpRestoreHandler(Board_ActionRestoreHP);
                        fighters[row, col].ActionLevelUp += new Fighter.EventLevelUpHandler(Board_ActionLevelUp);
                    }
        }

        private void AddPawns(int row)
        {
            fighters[row, 1] = new Pawn(
                Owner.White,
                new Square(row, 1, SquareType.DontCare));
            fighters[row, 6] = new Pawn(
                Owner.Black,
                new Square(row, 6, SquareType.DontCare));
        }

        private void AddRooks(int row)
        {
            fighters[row, 0] = new Rook(
                Owner.White,
                new Square(row, 0, SquareType.DontCare));
            fighters[row, 7] = new Rook(
                Owner.Black,
                new Square(row, 7, SquareType.DontCare));
        }

        private void AddKnights(int row)
        {
            fighters[row, 0] = new Knight(
                Owner.White,
                new Square(row, 0, SquareType.DontCare));
            fighters[row, 7] = new Knight(
                Owner.Black,
                new Square(row, 7, SquareType.DontCare));
        }

        private void AddBishops(int row)
        {
            fighters[row, 0] = new Bishop(
                Owner.White,
                new Square(row, 0, SquareType.DontCare));
            fighters[row, 7] = new Bishop(
                Owner.Black,
                new Square(row, 7, SquareType.DontCare));
        }

        private void AddQueens(int row)
        {
            fighters[row, 0] = new Queen(
                Owner.White,
                new Square(row, 0, SquareType.DontCare));
            fighters[row, 7] = new Queen(
                Owner.Black,
                new Square(row, 7, SquareType.DontCare));
        }

        private void AddKings(int row)
        {
            fighters[row, 0] = new King(
                Owner.White,
                new Square(row, 0, SquareType.DontCare));
            fighters[row, 7] = new King(
                Owner.Black,
                new Square(row, 7, SquareType.DontCare));
        }

        private void SelectFighter(int row, int col)
        {
            fighters[row, col].Select();
            activeFighter = fighters[row, col];
        }

        private void MoveFighter(Fighter fighter, Move move)
        {
            fighters[move.OldRow, move.OldCol] = null;
            fighter.Stance = Stance.Sit;
            fighters[move.NewRow, move.NewCol] = fighter;
            UpdateNextPosAllFighters();
        }

        private void ProcessMoveToRandomSquare()
        {
            int row = activeFighter.CurrentPos.Row;
            int col = activeFighter.CurrentPos.Col;

            Random rand = new Random();
            int fateNumber = (rand.Next(20) % 4) + 1;

            switch (fateNumber)
            {
                case 1:
                    // die
                    FighterMoveToRandomSquare(activeFighter, SquareType.Die);
                    fighters[row, col].KilledBy(null);
                    activeFighter = null;
                    break;
                case 2:
                    // hp restore
                    FighterMoveToRandomSquare(activeFighter, SquareType.HpRestore);
                    fighters[row, col].RestoreHP();
                    break;
                case 3:
                    // die
                    FighterMoveToRandomSquare(activeFighter, SquareType.Die);
                    fighters[row, col].KilledBy(null);
                    activeFighter = null;
                    break;
                case 4:
                    // level up
                    FighterMoveToRandomSquare(activeFighter, SquareType.LevelUp);
                    fighters[row, col].LevelUp(fighters[row, col].Level);
                    break;
            }
        }

        private void DeselectActiveFighter()
        {
            activeFighter.SitDown();
            activeFighter = null;
            return;
        }

        private void FighterArriveAt(int row, int col)
        {
            switch (squares[row, col].Type)
            {
                case SquareType.HpRestoreWhite:
                    if (activeFighter.Owner == Owner.White)
                        fighters[row, col].RestoreHP();
                    break;
                case SquareType.HpRestoreBlack:
                    if (activeFighter.Owner == Owner.Black)
                        fighters[row, col].RestoreHP();
                    break;
                case SquareType.LevelUpWhite:
                    if (activeFighter.Owner == Owner.White)
                        fighters[row, col].LevelUp(fighters[row, col].Level); // double level -- TOO MUCH ???...
                    break;
                case SquareType.LevelUpBlack:
                    if (activeFighter.Owner == Owner.Black)
                        fighters[row, col].LevelUp(fighters[row, col].Level); // double level
                    break;
                case SquareType.Random:
                    ProcessMoveToRandomSquare();
                    break;
                default: break;
            }
        }

        private void ChangeTurn()
        {
            if (activePlayer == Owner.White)
                activePlayer = Owner.Black;
            else if (activePlayer == Owner.Black)
                activePlayer = Owner.White;

            if (activePlayer == player1.Owner)
            {
                player1.Active = true;
                player2.Active = false;
            }
            else if (activePlayer == player2.Owner)
            {
                player2.Active = true;
                player1.Active = false;
            }
        }

        private void EndGame(Owner loser)
        {
            if (player1.Owner == loser)
            {
                this.winner = player2;
                this.loser = player1;
                GameOver(player2, player1);
            }
            else if (player2.Owner == loser)
            {
                this.winner = player1;
                this.loser = player2;
                GameOver(player1, player2);
            }
        }
        #endregion
    }
}
